﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/9 10:27:08
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ImageK.Java
{
    /// <summary>
    /// java wrapper
    /// </summary>
    public class IndexColorModel : ColorModel
    {
        private int[] rgb;
        private int map_size;
        private int pixel_mask;
        private int transparent_index = -1;
        private bool allgrayopaque;
        private static int[] opaqueBits = { 8, 8, 8 };
        private static int[] alphaBits = { 8, 8, 8, 8 };
        /**
         * Constructs an {@code IndexColorModel} from the specified
         * arrays of red, green, and blue components.  Pixels described
         * by this color model all have alpha components of 255
         * unnormalized (1.0&nbsp;normalized), which means they
         * are fully opaque.  All of the arrays specifying the color
         * components must have at least the specified number of entries.
         * The {@code ColorSpace} is the default sRGB space.
         * Since there is no alpha information in any of the arguments
         * to this constructor, the transparency value is always
         * {@code Transparency.OPAQUE}.
         * The transfer type is the smallest of {@code DataBuffer.TYPE_BYTE}
         * or {@code DataBuffer.TYPE_USHORT} that can hold a single pixel.
         * @param bits      the number of bits each pixel occupies
         * @param size      the size of the color component arrays
         * @param r         the array of red color components
         * @param g         the array of green color components
         * @param b         the array of blue color components
         * @throws IllegalArgumentException if {@code bits} is less
         *         than 1 or greater than 16
         * @throws IllegalArgumentException if {@code size} is less
         *         than 1
         */
        public IndexColorModel(int bits, int size, byte[] r, byte[] g, byte[] b):base(bits, size, r, g, b)
        {
            if (bits < 1 || bits > 16)
            {
                throw new ArgumentException("Number of bits must be between"
                                            + " 1 and 16.");
            }

            setRGBs(size, r, g, b, null);
            calculatePixelMask();
        }

        private void setRGBs(int size, byte[] r, byte[] g, byte[] b, byte[] a)
        {
            if (size < 1)
            {
                throw new ArgumentException("Map size ("+size+
                                            ") must be >= 1");
            }
            map_size = size;
            rgb = new int[calcRealMapSize(pixel_bits, size)];
            int alpha = 0xff;
            int transparency = OPAQUE;
            bool allgray = true;
            for (int i = 0; i < size; i++)
            {
                int rc = r[i] & 0xff;
                int gc = g[i] & 0xff;
                int bc = b[i] & 0xff;
                allgray = allgray && (rc == gc) && (gc == bc);
                if (a != null)
                {
                    alpha = a[i] & 0xff;
                    if (alpha != 0xff)
                    {
                        if (alpha == 0x00)
                        {
                            if (transparency == OPAQUE)
                            {
                                transparency = BITMASK;
                            }
                            if (transparent_index < 0)
                            {
                                transparent_index = i;
                            }
                        }
                        else
                        {
                            transparency = TRANSLUCENT;
                        }
                        allgray = false;
                    }
                }
                rgb[i] = (alpha << 24) | (rc << 16) | (gc << 8) | bc;
            }
            this.allgrayopaque = allgray;
            setTransparency(transparency);
        }

        private int calcRealMapSize(int bits, int size)
        {
            int newSize = Math.Max(1 << bits, size);
            return Math.Max(newSize, 256);
        }

        /**
         * Returns the size of the color/alpha component arrays in this
         * {@code IndexColorModel}.
         * @return the size of the color and alpha component arrays.
         */
        public int getMapSize()
        {
            return map_size;
        }

        /**
         * Copies the array of red color components into the specified array.
         * Only the initial entries of the array as specified by
         * {@link #getMapSize() getMapSize} are written.
         * @param r the specified array into which the elements of the
         *      array of red color components are copied
         */
        public void getReds(byte[] r)
        {
            for (int i = 0; i < map_size; i++)
            {
                r[i] = (byte)(rgb[i] >> 16);
            }
        }

        /**
         * Copies the array of green color components into the specified array.
         * Only the initial entries of the array as specified by
         * {@code getMapSize} are written.
         * @param g the specified array into which the elements of the
         *      array of green color components are copied
         */
        public void getGreens(byte[] g)
        {
            for (int i = 0; i < map_size; i++)
            {
                g[i] = (byte)(rgb[i] >> 8);
            }
        }

        /**
         * Copies the array of blue color components into the specified array.
         * Only the initial entries of the array as specified by
         * {@code getMapSize} are written.
         * @param b the specified array into which the elements of the
         *      array of blue color components are copied
         */
        public void getBlues(byte[] b)
        {
            for (int i = 0; i < map_size; i++)
            {
                b[i] = (byte)rgb[i];
            }
        }

        private void setTransparency(int transparency)
        {
            if (this.transparency != transparency)
            {
                this.transparency = transparency;
                if (transparency == OPAQUE)
                {
                    supportsAlpha = false;
                    numComponents = 3;
                    nBits = opaqueBits;
                }
                else
                {
                    supportsAlpha = true;
                    numComponents = 4;
                    nBits = alphaBits;
                }
            }
        }

        /**
         * This method is called from the constructors to set the pixel_mask
         * value, which is based on the value of pixel_bits.  The pixel_mask
         * value is used to mask off the pixel parameters for methods such
         * as getRed(), getGreen(), getBlue(), getAlpha(), and getRGB().
         */
        private void calculatePixelMask()
        {
            // Note that we adjust the mask so that our masking behavior here
            // is consistent with that of our native rendering loops.
            int maskbits = pixel_bits;
            if (maskbits == 3)
            {
                maskbits = 4;
            }
            else if (maskbits > 4 && maskbits < 8)
            {
                maskbits = 8;
            }
            pixel_mask = (1 << maskbits) - 1;
        }

        /**
         * Returns the red color component for the specified pixel, scaled
         * from 0 to 255 in the default RGB ColorSpace, sRGB.  The pixel value
         * is specified as an int.
         * Only the lower <em>n</em> bits of the pixel value, as specified in the
         * <a href="#index_values">class description</a> above, are used to
         * calculate the returned value.
         * The returned value is a non pre-multiplied value.
         * @param pixel the specified pixel
         * @return the value of the red color component for the specified pixel
         */
        public int getRed(int pixel)
        {
            return (rgb[pixel & pixel_mask] >> 16) & 0xff;
        }

        /**
         * Returns the green color component for the specified pixel, scaled
         * from 0 to 255 in the default RGB ColorSpace, sRGB.  The pixel value
         * is specified as an int.
         * Only the lower <em>n</em> bits of the pixel value, as specified in the
         * <a href="#index_values">class description</a> above, are used to
         * calculate the returned value.
         * The returned value is a non pre-multiplied value.
         * @param pixel the specified pixel
         * @return the value of the green color component for the specified pixel
         */
        public int getGreen(int pixel)
        {
            return (rgb[pixel & pixel_mask] >> 8) & 0xff;
        }

        /**
         * Returns the blue color component for the specified pixel, scaled
         * from 0 to 255 in the default RGB ColorSpace, sRGB.  The pixel value
         * is specified as an int.
         * Only the lower <em>n</em> bits of the pixel value, as specified in the
         * <a href="#index_values">class description</a> above, are used to
         * calculate the returned value.
         * The returned value is a non pre-multiplied value.
         * @param pixel the specified pixel
         * @return the value of the blue color component for the specified pixel
         */
        public int getBlue(int pixel)
        {
            return rgb[pixel & pixel_mask] & 0xff;
        }

        /**
         * Returns the alpha component for the specified pixel, scaled
         * from 0 to 255.  The pixel value is specified as an int.
         * Only the lower <em>n</em> bits of the pixel value, as specified in the
         * <a href="#index_values">class description</a> above, are used to
         * calculate the returned value.
         * @param pixel the specified pixel
         * @return the value of the alpha component for the specified pixel
         */
        public int getAlpha(int pixel)
        {
            return (rgb[pixel & pixel_mask] >> 24) & 0xff;
        }
    }
}
